﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using AtomicTypes;
using QLabValues;
using ZedGraph;
using System.Drawing.Printing;

namespace NumericalCalculations
{
    public sealed partial class Figure : Form
    {
        //To determine which is the last used figure, we double link the figures
        public static Figure HeadFigure;
        public Figure NextFigure;
        public Figure PreviousFigure;

        public Figure()
        {
            InitializeComponent();
            if (HeadFigure != null)
            {
                HeadFigure.NextFigure = this;
                PreviousFigure = HeadFigure;
            }
            HeadFigure = this;
            ColorOrder = new List<Color>
                             {
                                 Color.FromArgb(0, 0, 255),
                                 Color.FromArgb(0, 127, 0),
                                 Color.FromArgb(255, 0, 0),
                                 Color.FromArgb(0, 192, 192),
                                 Color.FromArgb(192, 0, 192),
                                 Color.FromArgb(192, 192, 0),
                                 Color.FromArgb(64, 64, 64),
                             };
            Graph.GraphPane.Title.IsVisible = false;
            Graph.GraphPane.XAxis.Title.IsVisible = false;
            Graph.GraphPane.YAxis.Title.IsVisible = false;
            //Graph.GraphPane.Legend.Position = LegendPos.InsideTopRight;
        }

        private int _index;
        private bool _autoAxis = true;
        private bool _legendVisible = true;
        private bool _legendBoxVisible = true;

        public int Index 
        {
            get { return _index; } 
            set
            {
                _index = value;
                Text = String.Format("Figure {0}", _index);
            }
        }

        public bool Hold
        {
            get;
            set;
        }

        public bool AutoAxis
        {
            get { return _autoAxis; }
            set { _autoAxis = value; }
        }

        public bool LegendVisible
        {
            get { return _legendVisible; }
            set
            {
                _legendVisible = value;
                if (value)
                {
                    //If there are no labels, invent all off them
                    if (Graph.GraphPane.CurveList.All(curve => curve.Label.Text == ""))
                    {
                        int i = 0;
                        foreach (var curve in Graph.GraphPane.CurveList)
                        {
                            curve.Label.Text = String.Format("data{0}", ++i);
                          
                        }
                    }
                }


                Graph.GraphPane.Legend.IsVisible = _legendVisible;
                RefreshGraph();
            }
        }

        public bool LegendBoxVisible
        {
            get { return _legendBoxVisible; }
            set
            {
                if (value != _legendBoxVisible)
                {
                    _legendBoxVisible = value;

                    Graph.GraphPane.Legend.Border = new Border(value, Color.Black, 1);
                    Graph.GraphPane.Legend.Fill = new Fill(value? Brushes.White : Brushes.Transparent);
                    RefreshGraph();
                }
            }
        }

        private void ToolStripButtonSaveClick(object sender, EventArgs e)
        {
            SaveFigure();
        }

        private void SaveFigure()
        {
            Bitmap bmp = new Bitmap(this.Graph.Width, this.Graph.Height);
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|Bitmap (*.bmp)|*.bmp|PNG (*.png)|*.png|Gif (*.gif)|*.gif";
            if(DialogResult.OK == saveFileDialog1.ShowDialog())
            {
                string name = saveFileDialog1.FileName;

                this.Graph.DrawToBitmap(bmp, new Rectangle(0, 0, this.Graph.Width, this.Graph.Height));
                switch (saveFileDialog1.FilterIndex)
                {
                    case 1:
                        bmp.Save(name, System.Drawing.Imaging.ImageFormat.Jpeg);
                        break;
                    case 2:
                        bmp.Save(name, System.Drawing.Imaging.ImageFormat.Bmp);
                        break;
                    case 3:
                        bmp.Save(name, System.Drawing.Imaging.ImageFormat.Png);
                        break;
                    case 4:
                        bmp.Save(name, System.Drawing.Imaging.ImageFormat.Gif);
                        break;

                }
            }
            
        }

        private void FigureFormClosed(object sender, FormClosedEventArgs e)
        {
            if (NextFigure != null)
            {
                NextFigure.PreviousFigure = PreviousFigure;
            }
            else
            {
                //we just closed the head figure, so we need to switch to the previous figure
                HeadFigure = PreviousFigure;
            }

            if (PreviousFigure != null)
            {
                PreviousFigure.NextFigure = NextFigure;
            }

        }

        public void Clear(bool reset)
        {
            Graph.GraphPane.CurveList = new CurveList();
            SetTitle("");
            SetXLabel("");
            SetYLabel("");
            SetAxisRange(Plotter.AxisRangeMode.Tight);
            RefreshGraph();
            if (reset)
            {
                BringToFront();
            }
        }

        public void SetTitle(string title)
        {
            Graph.GraphPane.Title.Text = title;
            Graph.GraphPane.Title.IsVisible = title != "";
            RefreshGraph();
        }

        public static List<Color> ColorOrder { get; set; }

        public bool AxisVisibility
        {
            //TODO: Implement axis visibility
            get
            {
                throw (new NumericalCalculationsException("Not implemented."));
            }
            set
            {
                throw (new NumericalCalculationsException("Not implemented."));
            }
        }

        public void Plot(double[] x, double[] y, Color? color, Plotter.PointTypes pointType, Plotter.LineTypes lineType, int colorIndex)
        {
            if(!Hold)
                Clear(false);

            Color c = color != null ? color.Value : ColorOrder[colorIndex % ColorOrder.Count];

            Graph.GraphPane.AddCurve(string.Empty, new PointPairList(x, y), c, PointTypeToSymbolType(pointType));
            
            if(!(Hold && !AutoAxis))
                Graph.GraphPane.AxisChange();
        }
        public void Fill(double[] x, double[] y, Color? color,Color?fillColor, Plotter.PointTypes pointType, Plotter.LineTypes lineType, int colorIndex)
        {
            if (!Hold)
                Clear(false);

            Color c = color != null ? color.Value : ColorOrder[colorIndex % ColorOrder.Count];
            Color fc = fillColor != null ? fillColor.Value : ColorOrder[colorIndex % ColorOrder.Count];
            //LineItem myCurve = myPane.AddCurve("Porsche",         list1, Color.Red, SymbolType.Diamond);

           LineItem myCurve= Graph.GraphPane.AddCurve(string.Empty, new PointPairList(x, y), c, PointTypeToSymbolType(pointType));
           myCurve.Line.Fill = new Fill(fc,c);
          

            if (!(Hold && !AutoAxis))
                Graph.GraphPane.AxisChange();
        }

        public void RefreshGraph()
        {
            Graph.Invalidate();            
        }


        private static SymbolType PointTypeToSymbolType(Plotter.PointTypes pointType)
        {
            switch (pointType)
            {
                case Plotter.PointTypes.None:
                    return SymbolType.None;
                case Plotter.PointTypes.Point:
                    return SymbolType.Default;
                case Plotter.PointTypes.Circle:
                    return SymbolType.Circle;
                case Plotter.PointTypes.XMark:
                    return SymbolType.XCross;
                case Plotter.PointTypes.Plus:
                    return SymbolType.Plus;
                case Plotter.PointTypes.Star:
                    return SymbolType.Star;
                case Plotter.PointTypes.Square:
                case Plotter.PointTypes.Pentagram: //not supported by ZedGraph
                case Plotter.PointTypes.Hexagram: //not supported by ZedGraph
                    return SymbolType.Square;
                case Plotter.PointTypes.Diamond:
                    return SymbolType.Diamond;
                case Plotter.PointTypes.TriangleDown:
                    return SymbolType.TriangleDown;
                case Plotter.PointTypes.TriangleUp: //not supported by ZedGraph
                case Plotter.PointTypes.TriangleLeft: //not supported by ZedGraph
                case Plotter.PointTypes.TriangleRight: //not supported by ZedGraph
                    return SymbolType.Triangle;
                default:
                    return SymbolType.Default;
            }
        }

        public void SetXLabel(string label)
        {
            Graph.GraphPane.XAxis.Title.Text = label;
            Graph.GraphPane.XAxis.Title.IsVisible = label != "";
            RefreshGraph();
        }

        public void SetYLabel(string label)
        {
            Graph.GraphPane.YAxis.Title.Text = label;
            Graph.GraphPane.YAxis.Title.IsVisible = label != "";
            RefreshGraph();
        }

        /// <summary>
        /// Sets legend texts for curves
        /// </summary>
        /// <param name="labels"></param>
        /// <returns>Returnes warning text</returns>
        public string SetLegend(List<string> labels)
        {
            for (int i = 0; i < Graph.GraphPane.CurveList.Count; i++)
            {
                Graph.GraphPane.CurveList[i].Label.Text = i < labels.Count? labels[i] : string.Empty;
            }
            RefreshGraph();
            return labels.Count > Graph.GraphPane.CurveList.Count ? "Warning: Ignoring extra legend entries." : string.Empty;
        }

        public void SetAxisRange(double xmin, double xmax, double ymin, double ymax)
        {
            if(xmax <= xmin || ymax <= ymin)
                throw (new NumericalCalculationsException("Values must be increasing."));

            SetAxisRange(Plotter.AxisRangeMode.Manual);
            Graph.GraphPane.XAxis.Scale.Min = xmin;
            Graph.GraphPane.XAxis.Scale.Max = xmax;
            Graph.GraphPane.YAxis.Scale.Min = ymin;
            Graph.GraphPane.YAxis.Scale.Max = ymax;
            RefreshGraph();
        }

        private Plotter.GridMode _gridMode = Plotter.GridMode.None;
        public void SetGrid(Plotter.GridMode mode)
        {
            switch (mode)
            {
                case Plotter.GridMode.None:
                    Graph.GraphPane.XAxis.MinorGrid.IsVisible = false;
                    Graph.GraphPane.YAxis.MinorGrid.IsVisible = false;
                    Graph.GraphPane.XAxis.MajorGrid.IsVisible = false;
                    Graph.GraphPane.YAxis.MajorGrid.IsVisible = false;
                    _gridMode = Plotter.GridMode.None;
                    break;
                case Plotter.GridMode.Major:
                    Graph.GraphPane.XAxis.MajorGrid.IsVisible = true;
                    Graph.GraphPane.YAxis.MajorGrid.IsVisible = true;
                    if (_gridMode != Plotter.GridMode.Minor)
                        _gridMode = Plotter.GridMode.Major;
                    break;
                case Plotter.GridMode.Minor:
                    Graph.GraphPane.XAxis.MajorGrid.IsVisible = true;
                    Graph.GraphPane.YAxis.MajorGrid.IsVisible = true;
                    Graph.GraphPane.XAxis.MinorGrid.IsVisible = true;
                    Graph.GraphPane.YAxis.MinorGrid.IsVisible = true;
                    _gridMode = Plotter.GridMode.Minor;
                    break;
            }
            RefreshGraph();
        }

        private void ToolStripButtonPrintClick(object sender, EventArgs e)
        {
          Graph.DoPrint();    
        }

        private void ToolStripButtonZoomInClick(object sender, EventArgs e)
        {
            Graph.ZoomPane(Graph.GraphPane, 9.0/10, new PointF(0, 0), false);
        }

        private void ToolStripButtonZoomOutClick(object sender, EventArgs e)
        {
            Graph.ZoomPane(Graph.GraphPane, 10.0/9, new PointF(0, 0), false);
        }

        public List<IQLabValue> GetAxis()
        {
            var res = new List<IQLabValue>();
            res.Add(new QFloat64NDimArray(new QFloat64[] 
            {
                new QFloat64(Graph.GraphPane.XAxis.Scale.Min),
                new QFloat64(Graph.GraphPane.XAxis.Scale.Max),
                new QFloat64(Graph.GraphPane.YAxis.Scale.Min),
                new QFloat64(Graph.GraphPane.YAxis.Scale.Max)
            }, new int[] { 1, 4 }));
            return res;
        }

        public void SetAxisRange(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax)
        {
            if (DialogResult.Yes == MessageBox.Show(
                String.Format("Sorry, ZedGraph does not support 3D....\r\n" +
                "Do you want to set 2D axis range with [{0}, {1}, {2}, {3}]?",
                xmin,xmax,ymin,ymax),
                "Sorry", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1))
            {
                SetAxisRange(xmin, xmax, ymin, ymax);
            }
        }

        public void SetAxisRange(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, double cmin, double cmax)
        {
            if (DialogResult.Yes == MessageBox.Show(
                String.Format("Sorry, ZedGraph does not support 3D and color scale....\r\n" +
                "Do you want to set 2D axis range with [{0}, {1}, {2}, {3}]?",
                xmin,xmax,ymin,ymax),
                "Sorry", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1))
            {
                SetAxisRange(xmin, xmax, ymin, ymax);
            }
        }

        public void SetAxisRange(Plotter.AxisRangeMode mode)
        {
            switch (mode)
            {
                case Plotter.AxisRangeMode.Auto:
                    Graph.GraphPane.XAxis.Scale.MinAuto = true;
                    Graph.GraphPane.XAxis.Scale.MaxAuto = true;
                    Graph.GraphPane.XAxis.Scale.MaxGrace = ZedGraph.Scale.Default.MaxGrace;
                    Graph.GraphPane.YAxis.Scale.MinAuto = true;
                    Graph.GraphPane.YAxis.Scale.MaxAuto = true;
                    Graph.GraphPane.YAxis.Scale.MaxGrace = ZedGraph.Scale.Default.MaxGrace;
                    Graph.GraphPane.AxisChange();
                    AutoAxis = true;
                    break;
                case Plotter.AxisRangeMode.Tight:
                case Plotter.AxisRangeMode.Image:
                    Graph.GraphPane.XAxis.Scale.MinGrace = 0;
                    Graph.GraphPane.XAxis.Scale.MaxGrace = 0;
                    Graph.GraphPane.YAxis.Scale.MinGrace = 0;
                    Graph.GraphPane.YAxis.Scale.MaxGrace = 0;
                    Graph.GraphPane.XAxis.Scale.MinAuto = true;
                    Graph.GraphPane.XAxis.Scale.MaxAuto = true;
                    Graph.GraphPane.YAxis.Scale.MinAuto = true;
                    Graph.GraphPane.YAxis.Scale.MaxAuto = true;
                    Graph.GraphPane.AxisChange();
                    AutoAxis = true;
                    break;
                case Plotter.AxisRangeMode.Manual:
                    Graph.GraphPane.XAxis.Scale.MinGrace = 0;
                    Graph.GraphPane.XAxis.Scale.MaxGrace = 0;
                    Graph.GraphPane.YAxis.Scale.MinGrace = 0;
                    Graph.GraphPane.YAxis.Scale.MaxGrace = 0;
                    Graph.GraphPane.XAxis.Scale.MinAuto = false;
                    Graph.GraphPane.XAxis.Scale.MaxAuto = false;
                    Graph.GraphPane.YAxis.Scale.MinAuto = false;
                    Graph.GraphPane.YAxis.Scale.MaxAuto = false;
                    Graph.GraphPane.AxisChange();
                    AutoAxis = false;
                    break;
                case Plotter.AxisRangeMode.XY:
                    break;
                default:
                    //TODO: Implement other scale modes
                    throw (new NumericalCalculationsException("Not implemented."));
                    break;
            }
            RefreshGraph();
        }
        public void Pie()
        {
           

            SetTitle("pie");

            // Fill the pane background with a color gradient
            Graph.GraphPane.Fill = new Fill(Color.White, Color.Goldenrod, 45.0f);
            // No fill for the chart background
            Graph.GraphPane.Chart.Fill.Type = FillType.None;

            // Set the legend to an arbitrary location
            Graph.GraphPane.Legend.Position = LegendPos.Float;
            Graph.GraphPane.Legend.Location = new Location(0.95f, 0.15f, CoordType.PaneFraction,
                           AlignH.Right, AlignV.Top);
            Graph.GraphPane.Legend.FontSpec.Size = 10f;
            Graph.GraphPane.Legend.IsHStack = false;

            // Add some pie slices
            PieItem segment1 = Graph.GraphPane.AddPieSlice(20, Color.Navy, Color.White, 45f, 0, "North");
            PieItem segment3 = Graph.GraphPane.AddPieSlice(30, Color.Purple, Color.White, 45f, .0, "East");
            PieItem segment4 = Graph.GraphPane.AddPieSlice(10.21, Color.LimeGreen, Color.White, 45f, 0, "West");
            PieItem segment2 = Graph.GraphPane.AddPieSlice(40, Color.SandyBrown, Color.White, 45f, 0.2, "South");
            PieItem segment6 = Graph.GraphPane.AddPieSlice(250, Color.Red, Color.White, 45f, 0, "Europe");
            PieItem segment7 = Graph.GraphPane.AddPieSlice(1500, Color.Blue, Color.White, 45f, 0.2, "Pac Rim");
            PieItem segment8 = Graph.GraphPane.AddPieSlice(400, Color.Green, Color.White, 45f, 0, "South America");
            PieItem segment9 = Graph.GraphPane.AddPieSlice(50, Color.Yellow, Color.White, 45f, 0.2, "Africa");

            segment2.LabelDetail.FontSpec.FontColor = Color.Red;

            // Sum up the pie values                                                               
            CurveList curves = Graph.GraphPane.CurveList;
            double total = 0;
            for (int x = 0; x < curves.Count; x++)
                total += ((PieItem)curves[x]).Value;

            // Make a text label to highlight the total value
            TextObj text = new TextObj("Total 2004 Sales\n" + "$" + total.ToString() + "M",
                           0.18F, 0.40F, CoordType.PaneFraction);
            text.Location.AlignH = AlignH.Center;
            text.Location.AlignV = AlignV.Bottom;
            text.FontSpec.Border.IsVisible = false;
            text.FontSpec.Fill = new Fill(Color.White, Color.FromArgb(255, 100, 100), 45F);
            text.FontSpec.StringAlignment = StringAlignment.Center;
            Graph.GraphPane.GraphObjList.Add(text);

            // Create a drop shadow for the total value text item
            TextObj text2 = new TextObj(text);
            text2.FontSpec.Fill = new Fill(Color.Black);
            text2.Location.X += 0.008f;
            text2.Location.Y += 0.01f;
            Graph.GraphPane.GraphObjList.Add(text2);

            // Calculate the Axis Scale Ranges
            Graph.AxisChange();

        }
        public void Bar(List<double> xValues, QFloat64NDimArray Y, Color? color, double? width)
        {
            if (!Hold)
                Clear(false);

            int rows = Y.DimensionSize[0];
            int columns = Y.DimensionSize[1];
            var bars = new PointPairList[columns];

            for (int i = 0; i < rows; i++)
            {
                double x = xValues != null ? xValues[i] : i + 1;
                for (int j = 0; j < columns; j++)
                {
                    if (bars[j] == null)
                        bars[j] = new PointPairList();

                    bars[j].Add(x, Y.Data[i*columns + j].Real);
                }
            }

            for (int j = 0; j < columns; j++)
            {
                //TODO: Apply bar width if width != null
                Graph.GraphPane.AddBar(string.Empty, bars[j], color != null ? color.Value : ColorOrder[j%ColorOrder.Count]);
            }

            if (!(Hold && !AutoAxis))
                Graph.GraphPane.AxisChange();
        }

        public void ToggleGrid()
        {
            if(_gridMode == Plotter.GridMode.None)
                SetGrid(Plotter.GridMode.Major);
            else if(_gridMode == Plotter.GridMode.Major)
                SetGrid(Plotter.GridMode.None);
        }
    }
}
